Skip to main content

Git Scripting-Schnittstelle verwenden

CODESYS Git stellt eine Scripting-Schnittstelle für Git zur Verfügung. Im Folgenden werden Ihnen Beispiele zur Verwendung der Schnittstelle aufgezeigt. Weiter unten finden Sie außerdem Informationen zur textbasierten Ausgabe der Meldungen, die bei vielen Git-Operationen erzeugt werden.

Für weitere Informationen zum Arbeiten mit der Scripting-Schnittstelle siehe: Script Engine API Documentation

Voraussetzungen

Für die Ausführung der folgenden Beispiele wird vorausgesetzt:

  • CODESYS 3.5.19.30 oder höher

Zusätzlich werden die folgenden Komponenten benötigt:

  • CODESYS Library Documentation Support (für das Erstellen der Compiled Library)

  • CODESYS Git 1.6.0.0 oder höher

  • Eine lokale Git-Installation

Wichtig

Verwenden Sie möglichst SecureString-Passwörter

Für eine erhöhte Sicherheit sollten Passwörter als .NET-SecureString übergeben werden.

Die davon betroffenen git-Operationen sind: clone, fetch, pull, push

SecureStrings können in IronPython wie folgt erstellt werden. Das "Passwort“ sollte dabei selbst aus einem sicheren String stammen und nicht, wie hier zu Demonstrationszwecken, im Klartext im Skript stehen. Intern wird jedes übergebene Passwort sicher behandelt:

        from System.Security import SecureString 

        sec_str_password = SecureString()
        for c in "Passwort":    
            sec_str_password.AppendChar(c)

Für weitere Security-Maßnahmen bei der Verwendung von CODESYS Git siehe: Security für CODESYS Git

Vorbereitung

CODESYS Bibliothek

Da aktuell keine CODESYS-Bibliotheken in Git verwaltet werden, werden die Quellen einer CODESYS-Bibliothek benötigt. In den Beispielen wird die Bibliothek String Functions.library des Produkts CODESYS String Libraries verwendet.

Remote-Git-Repository

Für das vorliegende Beispiel wird ein Git-Bare-Repository im Dateisystem als Remote-Repository verwendet.

Zur Vorbereitung wird das entsprechende Verzeichnis zunächst gelöscht und dann neu angelegt.

import shutil
import os 

def prepare_empty_dir(empty_dir_path):
    print("Prepare empty directory at", empty_dir_path)
    shutil.rmtree(empty_dir_path, ignore_errors=True)
    if not(os.path.exists(empty_dir_path) and os.path.isdir(empty_dir_path)):
        os.makedirs(empty_dir_path)

Anschließend wird ein leeres Bare-Git-Repository angelegt.

import subprocess
def create_bare_git_repository(bare_repository_path):
    print("Create bare git repository at", bare_repository_path)
    create_bare_repository_cmd = 'cmd /c "git -C \"' + bare_repository_path + '\" init --bare"'
    try:
        retcode = subprocess.call(create_bare_repository_cmd, shell=True)
        if retcode < 0:
            raise Exception("Creating bare git repository at " + bare_repository_path + " failed: ", -retcode)
        else:
            print("Creating bare git repository at " + bare_repository_path + " succeeded.")
    except Exception as e:
        print("[ERROR] Creating bare git repository failed: ", e)
        raise

Das angelegte leere Bare-Git-Repository wird mit dem Inhalt der CODESYS-Bibliothek befüllt.

def initialize_bare_git_repository(library_path, local_repository_path, bare_repository_path):
    print("Open library:", library_path)
    project = projects.open(library_path)
     
    print("Initiate local git repository")
    project.git.init(local_repository_path)
    project.git.commit_complete("Create git repo for lib", "user", "mail@mail")
     
    print("Push to remote git repository")
    origin_remote = project.git.remote_add("origin", bare_repository_path)
    project.git.branch_set_upstream_to(origin_remote)
    project.git.push()
    project.git.de_init(cleanUpFileSystem=True)
    project.close()

Das folgende Skript führt die beschriebenen Funktionen aus.

import os

def main():
    if projects.primary:
        projects.primary.close()

    basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\"

    project_basepath = os.path.join(basepath, "projects\\")
    library_file_name = "ExampleLib1.library"
    library_path = os.path.join(project_basepath, library_file_name)

    remote_repo_basepath = os.path.join(basepath, "remotes\\")
    remote_repo_directory_name = "ExampleLib1RemoteRepo"
    remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name)

    local_repo_basepath = os.path.join(basepath, "repos\\")
    local_repo_directory_name = "ExampleLib1LocalRepo"
    local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name)

    print("Create and push library to remote git repository")
    
    prepare_empty_dir(remote_repo_path)
    create_bare_git_repository(remote_repo_path)
    initialize_bare_git_repository(library_path, local_repo_path, remote_repo_path)

    print("[Success] All done")


if __name__ == '__main__':
    main()

Das auf diese Weise erstellte und mit Inhalt versehene Bare-Git-Repository wird für die weiteren Bespiele genutzt.

Clonen eines Git-Remote-Repository

Die folgende Funktion führt git clone für ein Git-Remote-Repository aus.

def clone_git_repository(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path):
    update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode    
    project = git.clone(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags)
    project.save()
    return project

Erstellen und Mergen eines neuen Branches

Die folgende Hilfsfunktion erzeugt exemplarisch einige neue Objekte in einem CODESYS-Projekt.

def add_dut(project):
    ST_STRUCT_STR = """\
        a : BOOL;
        b : BIT;
        c : BIT;
    """

    ST_UNION_STR = """\
    TYPE ExampleUnion :
    UNION
        Zahl : INT;
        Prozent : ExampleAlias;
        Bits : ExampleStruct;
    END_UNION
    END_TYPE
    """

   # Create a struct DUT and insert the list of variables just into the right
   # place in line two, row 0 (line numbering starts with line 0)
   example_dut_struct = project.create_dut('ExampleStruct') # DutType.Structure is the default
   example_dut_struct.textual_declaration.insert(2, 0, ST_STRUCT_STR) 

   # Alias types get their "content" via the base type, which will just end up 
   # as one line in the declaration part:    
   # TYPE MyAlias : INT (0..100); END_TYPE 
   example_dut_alias = project.create_dut('ExampleAlias', DutType.Alias, "INT (0..100)") 

   # Instead of injecting the variables into the existing declaration, 
   # one can also just replace the complete declaration part, including the 
   # boilerplate code.
   example_dut_union = project.create_dut('ExampleUnion', DutType.Union)
   example_dut_union.textual_declaration.replace(ST_UNION_STR)

Die folgende Hilfsfunktion inkrementiert die Build-Version in den Projektinformationen eines CODESYS-Projekts.

def increment_build_version(project):
    """
    Increment build version in project info.
    """
    info = project.get_project_info()
    old_version = info.version
    info.version = (old_version.Major, old_version.Minor, old_version.Build + 1, 0)
    project.save()

Die folgende Funktion erstellt zunächst einen neuen Branch, führt in diesem Branch Änderungen aus und merget anschließend diese Änderungen zurück in den Hauptbranch.

def copy_branch_and_merge(project):
    current_branch = project.git.branch_show_current()
    print("Current branch: ", current_branch.friendly_name)
    project, current_branch = project.git.branch_copy(current_branch, "new_branch", checkout=True)
    print("Current branch: ", current_branch.friendly_name)

    add_dut(project)
    project.git.commit_complete("Added DUT", "user", "mail@mail")

    increment_build_version(project)
    project.git.commit_complete("Incremented build version", "user", "mail@mail")

    project, current_branch = project.git.checkout("master")
    print("Current branch: ", current_branch.friendly_name)
    project, merge_result = project.git.merge("new_branch")
    print("Merged: ", merge_result.ToString())
    project.save()
    return project

Das folgende Skript führt git clone für ein Git-Remote-Repository aus, führt Änderungen im Projekt durch und pusht die Änderungen anschließend in das Git-Remote-Repository (CopyBranchAndMerge.py).

def main():
    if projects.primary: 
       projects.primary.close()

    basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\"

    project_basepath = os.path.join(basepath, "projects\\")
    library_file_name = "ExampleLib1Cloned.library"

    remote_repo_basepath = os.path.join(basepath, "remotes\\")
    remote_repo_directory_name = "ExampleLib1RemoteRepo"
    remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name)

    local_repo_basepath = os.path.join(basepath, "repos\\")
    local_repo_directory_name = "ExampleLib1LocalRepo"
    local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name)

    print("Clone project")
    project = clone_git_repository(project_basepath, library_file_name, remote_repo_path, local_repo_path)
    project = copy_branch_and_merge(project)
    project.git.push()
    project.save()
    project.git.de_init(cleanUpFileSystem=True)
    project.save()
    project.close()
    print("[Success] All done")

if __name__ == '__main__':
    main())

Erzeugen einer Compiled Library

Das folgende Skript führt git clone für eine CODESYS-Quellbibliothek aus einem Git-Remote-Repository aus und erstellt anschließend daraus eine compilierte Bibliothek (CreateCompiledLibrary.py).

import os

class CompileError(Exception):
    pass

def clone_git_repository(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path):
    update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode
    project = git.clone(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags)
    project.save()
    return project

def create_compiled_library(project):
    # requires the CODESYS Library Documentation Support Package!
    project.check_all_pool_objects()
    compile_result_message = system.get_messages(category='{97F48D64-A2A3-4856-B640-75C046E37EA9}')[-1]
    if "0 errors" in compile_result_message:
        project.save_as_compiled_library(destination_name=None)
    else:
        raise CompileError("Compile failed: " + compile_result_message)
    return project

basepath = "D:\\JiraTickets\\GIT-145\\"

project_basepath = os.path.join(basepath, "projects\\")

remote_repo_basepath = os.path.join(basepath, "remotes\\")
remote_repo_directory_name = "StringFunctions.git"
remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name)

local_repo_basepath = os.path.join(basepath, "repos\\")
local_repo_path = os.path.join(local_repo_basepath, "StringFunctions.git")

print("Clone project")
project = clone_git_repository(project_basepath, "String Functions Cloned.library", remote_repo_path, local_repo_path)
project = create_compiled_library(project)
project.git.de_init(cleanUpFileSystem=True)
project.close()

print("[Success] All done")

Installieren einer Bibliothek aus einem Git Remote Repository

Das folgende Skript führt git clone für eine CODESYS-Quellbibliothek aus einem Git-Remote-Repository aus und installiert diese Bibliothek in der aktuellen CODESYS-Instanz (InstallLibrary.py).

import os

def clone_git_repository(project_directory_path, project_file_name, remote_repo_url_or_path, local_repo_path):
    update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode
    project = git.clone(project_directory_path, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags)
    project.save()
    return project

def install_library(project):
    library_repo = librarymanager.repositories[0]
    librarymanager.install_library(project.path, library_repo, True)

def main():
    if projects.primary:
        projects.primary.close()

     basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\"

    project_basepath = os.path.join(basepath, "projects\\")
    library_file_name = "ExampleLib1Cloned2.library"

    remote_repo_basepath = os.path.join(basepath, "remotes\\")
    remote_repo_directory_name = "ExampleLib1RemoteRepo"
    remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name)

    local_repo_basepath = os.path.join(basepath, "repos\\")
    local_repo_directory_name = "ExampleLib1LocalRepo"
    local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name)

    print("Clone project")
    project = clone_git_repository(project_basepath, library_file_name, remote_repo_path, local_repo_path)

    print("Install library")
    install_library(project)
    project.git.de_init(cleanUpFileSystem=True)
    project.close()

    print("[Success] All done")

if __name__ == '__main__':
    main()

Meldungsausgaben bei Git-Operationen

Für die Nutzung von CODESYS Git liefern die meisten Befehle eine textbasierte Ausgabe. Diese wird im Betrieb von CODESYS in der Kommandozeile automatisch bei der Ausführung von CODESYS Git-Befehlen über den Script-Treiber ausgegeben. Beim Verwenden von CODESYS Git in CODESYS Development System erscheinen die Ausgaben auch im Meldungsfenster.

Aufbau der Meldungen: Git: <severity>: [<time>] <text>

In der Benutzeroberfläche von CODESYS Git reduziert sich die Ausgabe auf: [<time>] <text>

  • severity: Meldungskategorie. Die Kategorien reichen von reiner Information bis zu kritischem Fehler

  • time: Genauer Zeitpunkt der Meldung. Format HH:MM:SS

  • text: Inhalt der Meldung. Bei Standard-Git-Befehlen entspricht der Inhalt dem Kommandozeilen-Befehl, der dasselbe Resultat liefern würde wie der getätigte Script-Treiber-Aufruf. Bei CODESYS Git-Befehlen, die keinem Standard-Git-Befehl entsprechen, beispielsweise Projekt aus Repository neu erstellen, erklärt der Meldungstext die ausgeführte Aktion.

Befehle mit mehreren Meldungen:

Für manche Befehle (beispielsweise git log wird die Ausgabe auf mehrere Meldungen aufgeteilt. Im Fall von git log wird jeder angezeigte Commit in einer eigenen Meldung angezeigt. Um in diesem Fall zu verdeutlichen, dass diese Meldungen Teil des git log-Befehls sind, wird in den Meldungen auf den ursprünglichem Befehl verwiesen.